package wellplatescanner;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;

import java.util.Enumeration;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

// TODO: make sure all threads are shut down correctly at stop


public class MainWindow  {

	protected Shell shlWellPlateScanner;
	private ScannerDevice scanner;
	private ArduinoDevice arduino;
	private Session session;
	private Project project;
	
	Display display;
	
	private ProjectComposite projectComposite;
	private Combo serialPort;
	private Text log;
	private Text txtShaker;
	private Text txtScanner;
	private Button btnStopSession;
	
	final Runnable onConnectionChangedRunnable = new Runnable() {
	      public void run() {
	    	onConnectionChanged();
	      }
	    };
	private Text arduinoStatus;
	private Text scannerStatus;
	

	/**
	 * Launch the application.
	 * @param args
	 */
	public static void main(String[] args) {
		
		try {
			MainWindow window = new MainWindow();
			window.open();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	/**
	 * Open the window.
	 */
	public void open() {
		
		scanner = new ScannerDevice();
		scanner.setChangeListener(new ScannerDevice.IDeviceChangedListener() {
			@Override
			public void scanCompleted() {
			}
			
			@Override
			public void connected() {
				display.syncExec(onConnectionChangedRunnable);
			}
		});

		arduino = new ArduinoDevice();
		arduino.setDeviceChangedListener(new ArduinoDevice.IDeviceChangedListener() {
			@Override
			public void stateChanged(int state) {
			}
			
			@Override
			public void connected() {
				display.syncExec(onConnectionChangedRunnable);
			}
		});
		
		
		session = new Session(arduino, scanner, this);
		
		display = Display.getDefault();
		createContents();
		shlWellPlateScanner.open();
		shlWellPlateScanner.layout();

		startup();
		
		while (!shlWellPlateScanner.isDisposed()) {
			
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
		
		arduino.close();
	}

	/**
	 * Create contents of the window.
	 */
	protected void createContents() {
		shlWellPlateScanner = new Shell();
		shlWellPlateScanner.setText("Well Plate Scanner");
		shlWellPlateScanner.setSize(414, 509);
		shlWellPlateScanner.setLayout(null);
		
		CLabel lblNewLabel = new CLabel(shlWellPlateScanner, SWT.NONE);
		lblNewLabel.setBounds(10, 7, 55, 19);
		lblNewLabel.setText("Arduino");
		
		arduinoStatus = new Text(shlWellPlateScanner, SWT.BORDER);
		arduinoStatus.setEditable(false);
		arduinoStatus.setBounds(79, 7, 99, 19);
		setDeviceStatus(arduinoStatus,false);
		
		serialPort = new Combo(shlWellPlateScanner, SWT.NONE);
		serialPort.setBounds(203, 7, 93, 21);
		serialPort.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				connectArduino();
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				connectArduino();
			}
		});
		updatePorts();

		
		CLabel lblScanner = new CLabel(shlWellPlateScanner, SWT.NONE);
		lblScanner.setText("Scanner");
		lblScanner.setBounds(10, 32, 55, 19);
		
		scannerStatus = new Text(shlWellPlateScanner, SWT.BORDER);
		scannerStatus.setEditable(false);
		scannerStatus.setBounds(78, 34, 100, 19);
		setDeviceStatus(scannerStatus,false);
		
		Label lblAsd = new Label(shlWellPlateScanner, SWT.SEPARATOR | SWT.HORIZONTAL);
		lblAsd.setBounds(10, 57, 380, 15);

		
		projectComposite = new ProjectComposite(shlWellPlateScanner, SWT.NULL);
		projectComposite.setBounds(10, 78, 418, 183);
		projectComposite.setLayout(null);
		
		txtShaker = new Text(shlWellPlateScanner, SWT.READ_ONLY);
		txtShaker.setBounds(26, 284, 68, 19);
		txtShaker.setText("Shaker");
		
		Button btnStartShake = new Button(shlWellPlateScanner, SWT.NONE);
		btnStartShake.setBounds(99, 279, 68, 23);
		btnStartShake.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				arduino.setRequestedState(ArduinoDevice.STATE_SHAKING);
			}
		});
		btnStartShake.setText("Start");
		
		Button btnStopShake = new Button(shlWellPlateScanner, SWT.NONE);
		btnStopShake.setBounds(169, 279, 68, 23);
		btnStopShake.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				arduino.setRequestedState(ArduinoDevice.STATE_STOPPED);
			}
		});
		btnStopShake.setText("Stop");
		
		txtScanner = new Text(shlWellPlateScanner, SWT.READ_ONLY);
		txtScanner.setBounds(26, 314, 68, 19);
		txtScanner.setText("Scanner");
		
		Button btnSingleScan = new Button(shlWellPlateScanner, SWT.NONE);
		btnSingleScan.setBounds(99, 309, 68, 23);
		btnSingleScan.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				projectComposite.updateProject(project);
				scanner.aquire(project.path, project.scannerSettings);
			}
		});
		btnSingleScan.setText("Single Scan");
		
		Button btnStartSession = new Button(shlWellPlateScanner, SWT.NONE);
		btnStartSession.setBounds(169, 309, 68, 23);
		btnStartSession.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				projectComposite.updateProject(project);
				session.start(project);
			}
		});
		btnStartSession.setText("Start Session");
		
		btnStopSession = new Button(shlWellPlateScanner, SWT.NONE);
		btnStopSession.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				session.stop();
			}
		});
		btnStopSession.setBounds(240, 309, 71, 23);
		btnStopSession.setText("Stop Session");
		
		Label label = new Label(shlWellPlateScanner, SWT.SEPARATOR | SWT.HORIZONTAL);
		label.setBounds(10, 339, 380, 15);
		
		log = new Text(shlWellPlateScanner, SWT.BORDER | SWT.V_SCROLL);
		log.setBounds(10, 360, 384, 104);

		   
		Log.addLogListener(new Log.LogListener() {
			@Override
			public void logAdded(final String s) {
				// make thread safe
				final Runnable print = new Runnable() {
				      public void run() {
				    	log.append(s);
				      }
				    };
				display.syncExec(print);
			}
		});
		
		
	}
	
	private void onConnectionChanged() {

		setDeviceStatus(arduinoStatus, arduino.isConnected());
		setDeviceStatus(scannerStatus, scanner.isConnected());

		if(!scanner.isConnected() || !arduino.isConnected()) {
			return;
		}
		
		if(project == null) {
			project = new Project();
			project.scannerSettings = scanner.getSettings();
			projectComposite.updateFromProject(project, scanner.getCapabilities());
		}
	}
		    
	private void updatePorts() {
		Enumeration ports = CommPortIdentifier.getPortIdentifiers();
		while (ports.hasMoreElements()) {
			CommPortIdentifier cpi = (CommPortIdentifier)ports.nextElement();
			if (cpi.getPortType() == CommPortIdentifier.PORT_SERIAL) {
				String name = cpi.getName();
				serialPort.add(name);
			}
		}
		if(serialPort.getItemCount() > 0) {
			serialPort.select(serialPort.getItemCount()-1);
		}
	}

	private void connectArduino() {
		String portName = serialPort.getItem(serialPort.getSelectionIndex());
		// Connect arduino
		try {
			CommPortIdentifier cpi = CommPortIdentifier.getPortIdentifier(portName);
			arduino.connect(cpi);
		} catch (NoSuchPortException e1) {
			Log.println("Faild to connect to port " + portName);
		}
	}
	
	private void startup() {

		connectArduino();		
		
		// Connect scanner
		scanner.connect();
		
	}

	public CommPortIdentifier getArduinoPort() {
		String portName = serialPort.getItem(serialPort.getSelectionIndex());
		try {
			return CommPortIdentifier.getPortIdentifier(portName);
		} catch (NoSuchPortException e1) {
			e1.printStackTrace();
		}
		return null;
	}
	
	private void setDeviceStatus(Text text, boolean connected ) {
		
		if(connected) {
			text.setText("CONNECTED");
			text.setBackground(new Color(text.getDisplay(),0,255,0));
		} else {
			text.setText("DISCONNECTED");
			text.setBackground(new Color(text.getDisplay(),255,0,0));
		}
	}
}
